package com.andova.tinker;

import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.widget.Toast;

import com.tencent.tinker.lib.service.DefaultTinkerResultService;
import com.tencent.tinker.lib.service.PatchResult;
import com.tencent.tinker.lib.util.TinkerServiceInternals;

import java.io.File;
import java.util.List;

import static com.tencent.tinker.lib.util.TinkerLog.e;
import static com.tencent.tinker.lib.util.TinkerLog.i;

/**
 * optional, you can just use DefaultTinkerResultService
 * we can restart process when we are at background or screen off
 * Created by zhangshaowen on 16/4/13.
 */
public class HotfixResultService extends DefaultTinkerResultService {
    private static final String TAG = "Tinker.TinkerResultService";

    public static final String INTENT_EXTRA_PATCH_RESULT = "patchResult";
    public static final String RECEIVER_ACTION_PATCH_RESULT = "com.tinker.action.PATCH_RESULT";

    @Override
    public void onPatchResult(final PatchResult result) {
        if (result == null) {
            e(TAG, "TinkerResultService received null result!!!!");
            return;
        }
        i(TAG, "TinkerResultService receive result: %s", result.toString());

        //first, we want to kill the recover process
        TinkerServiceInternals.killTinkerPatchServiceProcess(getApplicationContext());

        Intent intent = new Intent(RECEIVER_ACTION_PATCH_RESULT);
        intent.putExtra(INTENT_EXTRA_PATCH_RESULT, result);
        getApplication().sendBroadcast(intent);

        // is success and newPatch, it is nice to delete the raw file, and restart at once
        // for old patch, you can't delete the patch file
        if (!result.isSuccess) return;
        deleteRawPatchFile(new File(result.rawPatchFilePath));

        //not like TinkerResultService, I want to restart just when I am at background!
        //if you have not install tinker this moment, you can use TinkerApplicationHelper api
        if (!checkIfNeedKill(result)) {
            i(TAG, "I have already install the newly patch version!");
            return;
        }
        if (result.isSuccess) {
            Toast.makeText(this, "load patch successful!", Toast.LENGTH_SHORT).show();
            Intent i = getPackageManager().getLaunchIntentForPackage(getPackageName());
            if (i == null) return;
            i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(i);
            android.os.Process.killProcess(android.os.Process.myPid());
        } else {
            Toast.makeText(this, "load patch failure!", Toast.LENGTH_SHORT).show();
            return;
        }
        if (isAppInBackground(getApplicationContext())) {
            i(TAG, "it is in background, just restart process");
            restartProcess();
            return;
        }
        //we can wait process at background, such as onAppBackground
        //or we can restart when the screen off
        i(TAG, "tinker wait screen to restart process");
        screenState(getApplicationContext(), new IOnScreenOff() {
            @Override
            public void onScreenOff() {
                restartProcess();
            }
        });
    }

    /**
     * you can restart your process through service or broadcast
     */
    private void restartProcess() {
        i(TAG, "app is background now, i can kill quietly");
        //you can send service or broadcast intent to restart your process
        android.os.Process.killProcess(android.os.Process.myPid());
    }

    /**
     * 判断当前应用是在前台运行还是在后台
     */
    private boolean isAppInBackground(Context context) {
        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
            return isAppInBackgroundV21(context, manager);
        } else {
            List<ActivityManager.RunningTaskInfo> taskInfo = manager.getRunningTasks(1);
            ComponentName componentInfo = taskInfo.get(0).topActivity;
            return componentInfo != null && !componentInfo.getPackageName().equals(context.getPackageName());
        }
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private boolean isAppInBackgroundV21(Context context, ActivityManager manager) {
        List<RunningAppProcessInfo> runningProcesses = manager.getRunningAppProcesses();
        for (RunningAppProcessInfo processInfo : runningProcesses) {
            if (processInfo.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND)
                continue;
            for (String activeProcess : processInfo.pkgList) {
                if (activeProcess.equals(context.getPackageName())) return false;
            }
        }
        return true;
    }

    public void screenState(Context context, final IOnScreenOff onScreenOffInterface) {
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        context.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent in) {
                String action = in == null ? "" : in.getAction();
                i(TAG, "ScreenReceiver action [%s] ", action);
                if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                    if (onScreenOffInterface != null) onScreenOffInterface.onScreenOff();
                }
                context.unregisterReceiver(this);
            }
        }, filter);
    }

    interface IOnScreenOff {
        void onScreenOff();
    }
}